package is.server.model.xtool;

import is.common.interfaces.SectionAdapter;
import is.common.util.Regex;
import is.common.exception.DroppingNonEmptyDepartmentException;
import is.common.exception.RowNotFoundException;
import is.common.exception.RowsAreaIncorrectException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.LinkedList;

/**
 * @author aspr0
 *         Date: 07.05.12
 *         Time: 16:16
 */
public abstract class AbstractSectionAdapter implements SectionAdapter
{
    protected Node section;
    protected NodeList rows;
    protected SectionArbiter sectionArbiter;
    protected int rowCount;
    protected Integer id;


    public AbstractSectionAdapter(Node section, NodeList rows, SectionArbiter sectionArbiter)
    {
        this.section = section;
        this.rows = rows;
        this.sectionArbiter = sectionArbiter;

        rowCount = rows.getLength();

        id = 0;

        if (rowCount > 0)
        {
            String lastIdString = rows.item(rowCount - 1).getChildNodes().item(0).getTextContent();
            id = Integer.parseInt(lastIdString);
            id++;
        }
    }


    public String dropRows(String[] rowIds) throws DroppingNonEmptyDepartmentException
    {
        int rowCountBefore = rowCount;

        for (String rowId : rowIds)
        {
            for (int rowNum = 0; rowNum < rowCount; rowNum++)

            {
                Node row = rows.item(rowNum);
                String currentRowId = row.getChildNodes().item(0).getTextContent();

                if (currentRowId.equals(rowId))
                {
                    section.removeChild(row);
                    break;
                }
            }
            updateRowCount();
        }

        Integer droppedRowCount = rowCountBefore - rowCount;
        return droppedRowCount.toString();
    }

    /**
     * @param rowNumStart      area starts
     * @param howMany          and continues howMany rows
     * @param sectionRowsCount all rows we have in section
     * @return true if area exists
     */
    protected static void rowsAreaExists(int rowNumStart, int howMany, int sectionRowsCount)
            throws RowsAreaIncorrectException
    {
        /* if overlapping existing rows area */
        if (rowNumStart < 0 || howMany > sectionRowsCount || rowNumStart + howMany > sectionRowsCount)
        {
            throw new RowsAreaIncorrectException();
        }
    }

    protected boolean isThereSuchEntry(int entryNum, String entryValue)
    {
        for (int rowNum = 0; rowNum < rowCount; rowNum++)
        {
            if (entryValue.equals(rows.item(rowNum).getChildNodes().item(entryNum).getTextContent()))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns masks converted from user input (?*) to regular expressions (.{1}.*). Every mask completed with .*
     *
     * @param masks
     * @return
     */
    protected static String[] prepareMasks(String[] masks)
    {
        int i = 0;
        String[] regexMasks = new String[masks.length];

        for (String mask : masks)
        {
            regexMasks[i++] = mask.replaceAll("\\*", ".*").replaceAll("\\?", ".{1}") + ".*";
        }

        return regexMasks;
    }

    public String[][] findRows(String[] masks) throws RowNotFoundException
    {
        masks = prepareMasks(masks);
        LinkedList<String[]> rowsMatchedIds = new LinkedList<String[]>();

        String[][] rowsReplacementMade = getRowsReplacementsMade(0, rowCount);

        for (String[] rowRM : rowsReplacementMade)
        {
            boolean rowMatches = true;
            int maskNum = 0;

            for (String entry : rowRM)
            {
                if (!Regex.stringMatches(entry, masks[maskNum++]))
                {
                    rowMatches = false;
                    break;
                }
            }

            if (rowMatches)
            {
                rowsMatchedIds.add(rowRM);
            }
        }
        return rowsMatchedIds.toArray(new String[][]{});
    }

    protected abstract String[][] getRowsReplacementsMade(int rowNumStart, int howMany) throws RowNotFoundException;

    protected NodeList getRowById(String id) throws RowNotFoundException
    {
        for (int rowNum = 0; rowNum < rowCount; rowNum++)
        {
            NodeList rowNodeList = rows.item(rowNum).getChildNodes();
            if (rowNodeList.item(0).getTextContent().equals(id))
            {
                return rowNodeList;
            }
        }
        throw new RowNotFoundException();
    }

    /**
     * @return id for new row
     */
    protected String getId()
    {
        return id.toString();
    }

    protected void incId()
    {
        id++;
    }

    protected NodeList createRow(String[] entriesTagNames)
    {
        Node rowElement = sectionArbiter.createDomElement(Name.Tag.row);
        Node createdRow = section.appendChild(rowElement);

        for (String entryTagName : entriesTagNames)
        {
            Node entryElement = sectionArbiter.createDomElement(entryTagName);
            createdRow.appendChild(entryElement);
        }

        return createdRow.getChildNodes();
    }

    protected void updateRowCount()
    {
        rowCount = rows.getLength();
    }
}
